﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LambdaManAi
{
    class DfsPath : UserFunctions
    {

        public static object MainFunction(params object[] args)
        {
            return Code(() =>
                Pair(Pair(1, List()), Closure(StepFunction))
            );
        }

        public static object StepFunction(params object[] args)
        {
            // arguments
            const int stepState = 0;
            const int worldState = 1;

            Locals locals = new Locals(args);

            return Code(locals, () => 
                (int)GetItem(locals[stepState], 0) == 2
                    ?   Pair
                        (
                            Pair
                            (
                                IsEmptyList(GetTail(GetLastTupleItem(locals[stepState], 1))) ? 1 : 2, 
                                GetTail(GetLastTupleItem(locals[stepState], 1))
                            ),
                            GetItem(GetLastTupleItem(locals[stepState], 1), 0)
                        )
                    : StepFunction2(locals[stepState], locals[worldState])
                );
        }

        public static object StepFunction2(params object[] args)
        {
            // arguments
            const int stepState = 0;
            const int worldState = 1;

            // locals (should have numbers just after arguments!)
            const int map = 2;
            const int man = 3;
            const int manY = 4;
            const int manX = 5;
            //const int maxNeighbor = 6;
            const int path = 6;
            const int upcell = 7;
            const int rightcell = 8;
            const int downcell = 9;
            const int leftcell = 10;
            const int nextPath = 11;

            Locals locals = null;
            locals = new Locals(args)
            {
                {map, () => GetItem(locals[worldState], MapStatus)},
                {man, () => GetItem(locals[worldState], ManStatus)},
                {manY, () => GetItem(GetItem(locals[man], 1), 1)},
                {manX, () => GetItem(GetItem(locals[man], 1), 0)},
                //{maxNeighbor, () => GetMaxNeighbor(locals[map], locals[manY], locals[manX])},
                {path, () => PathToPill(locals[map], locals[manY], locals[manX], 0, -1)},
                //{curcell, () =>NormalizeCell(GetCell(locals[map], locals[manY], locals[manX]))},
                {upcell, () =>  NormalizeCell(GetCell(locals[map], (int)locals[manY]-1, locals[manX]))},
                {rightcell, () => NormalizeCell(GetCell(locals[map], locals[manY], (int)locals[manX]+1))},
                {downcell, () => NormalizeCell(GetCell(locals[map], (int)locals[manY]+1, locals[manX]))},
                {leftcell, () => NormalizeCell(GetCell(locals[map], locals[manY], (int)locals[manX]-1))},
                {nextPath, () => IsEmptyList(locals[path]) ? List() : GetTail(locals[path]) },
            };

            return Code(locals, () => 
                Pair(
                    Pair(IsEmptyList(locals[nextPath]) ? 1 : 2, locals[nextPath]), 

                    !IsEmptyList(locals[path]) ? GetItem(locals[path], 0) : 
                    ((int)locals[leftcell] != Wall ? MoveLeft :
                    ((int)locals[downcell] != Wall ? MoveDown :
                    ((int)locals[rightcell] != Wall ? MoveRight : MoveUp)))));
        }

        //Поиск в глубину пилюли, сначала вверх, потом вправо, потом вниз, потом влево
        public static object PathToPill(params object[] args)
        {
            const int map = 0;
            const int y = 1;
            const int x = 2;
            const int distance = 3;
            const int prevDirection = 4;
            //locals
            const int curcell = 5;
            const int nextDirections = 6;
            //const int nextCells = 7;

            Locals locals = null;
            locals = new Locals(args)
            {
                {curcell, () =>NormalizeCell(GetCell(locals[map], locals[y], locals[x]))},
                {nextDirections, () => (int)locals[prevDirection] == MoveUp ? List(MoveUp, MoveRight, MoveLeft)
                                     : (int)locals[prevDirection] == MoveRight ? List(MoveUp, MoveRight, MoveDown)
                                     : (int)locals[prevDirection] == MoveDown ? List(MoveRight, MoveDown, MoveLeft)
                                     : (int)locals[prevDirection] == MoveLeft ? List(MoveUp, MoveDown, MoveLeft)
                                     : List(MoveUp, MoveRight, MoveDown, MoveLeft)},
                //{nextCells, () => MapList(locals[nextDirections], Closure(GetCellNormAndDirectionInDirection(locals[map], locals[y], locals[x])))},
            };

            return Code(locals, () =>
                    Select((int)locals[distance] > 40 || (int)locals[curcell] == Wall, List(),
                        FoldList(
                            locals[nextDirections],
                        // accumulator - just simply check directions
                            FoldList(
                                locals[nextDirections],
                                List(),
                                Closure(
                                    Select(
                                        IsEmptyList(GetParam(args, 0)),
                                        (int)GetCellNorm(
                                            GetEnv(args, 1, map),
                                            NextY(GetEnv(args, 1, y), GetParam(args, 1)),
                                            NextX(GetEnv(args, 1, x), GetParam(args, 1))) > EmptySpace ? List(GetParam(args, 1)) : List(),
                                        GetParam(args, 0)
                                        )
                                )
                            ),

                            Closure(
                                Select(
                                    IsEmptyList(GetParam(args, 0)),
                                    AddElemIfListNotEmpty(
                                        GetParam(args, 1),
                                        PathToPill(
                                            GetEnv(args, 1, map),
                                            NextY(GetEnv(args, 1, y), GetParam(args, 1)),
                                            NextX(GetEnv(args, 1, x), GetParam(args, 1)),
                                            (int)GetEnv(args, 1, distance) + 1,
                                            GetParam(args, 1))
                                    ),
                                    GetParam(args, 0)
                                )
                            )
                         )
                    )
                );
        }
    }
}
